/*************************************************
* Title：TinyToolKit
* Author：Opportunity 
* Describe：
* CreateTime：2020-05-06 22:12:32
* Version：1.0
* Modify Recorder：
*************************************************/

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using UnityEngine;

namespace TinyToolKit.Network
{
    public class UdpReceive
    {
        private Socket _udpReceive;
        private byte[] _dataBuffer;
        private const int BUFFER_SIZE = 1024;
        private BroadcastPacket _feedbackPacket;
        private Action<BroadcastPacket> _onReceivedMsg;
        
        public Socket socket => _udpReceive; 

        public UdpReceive(BroadcastPacket packet,Action<BroadcastPacket> onReceivedMsg)
        {
            _dataBuffer = new byte[BUFFER_SIZE];
            _feedbackPacket = packet;
            _onReceivedMsg = onReceivedMsg;
        }

        public bool StartUdpReceive(int port)
        {
            try
            {
                _udpReceive = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                //IPAddress.Any 提供一个IP地址，指示服务器应该监听所有网络接口上的客户端活动
                var iep = new IPEndPoint(IPAddress.Any, port);  
                _udpReceive.Bind(iep);
                _udpReceive.BeginReceive(_dataBuffer, 0, BUFFER_SIZE, SocketFlags.None, OnUdpReceiveMsg, _udpReceive);
                TLogTool.Info("【UdpReceive】开始接收<b>{0}</b>发送的广播信息", iep.ToString());
                return true;
            }
            catch (Exception e)
            {
                TLogTool.Error("【UdpReceive】接收消息时发生错误：" + e);
                return false;
            }
        }
        
        private void OnUdpReceiveMsg(IAsyncResult ar)
        {
            Socket socket = ar.AsyncState as Socket;
            try
            {
                if (socket != null && _udpReceive != null)
                {
                    int count = socket.EndReceive(ar);
                    if (count <= 0)
                    {
                        Close();
                        return;
                    }
                    
                    string msg = Encoding.UTF8.GetString(_dataBuffer, 0, count);
                    string iep = socket.LocalEndPoint.ToString();
                    TLogTool.Info($"【UdpReceive】接收到<b>{iep}</b>发送的广播消息：{msg}");
                    _udpReceive.BeginReceive(_dataBuffer, 0, BUFFER_SIZE, SocketFlags.None, OnUdpReceiveMsg, _udpReceive);
                    
                    OnReceiveComplete(msg);
                }
            }
            catch (Exception e)
            {
                TLogTool.Error("【UdpReceive】接收消息时发生错误：" + e);
            }
        }
        
        public void SendMsg(IPEndPoint iPEndPoint,byte[] data)  
        {
            try
            {
                if (_udpReceive != null)
                {
                    TLogTool.Info($"【UdpReceive】向<b>{iPEndPoint}</b>发送反馈消息：" + Encoding.UTF8.GetString(data));
                    _udpReceive.SendTo(data, iPEndPoint);
                } 
            }
            catch (Exception ex)
            {
                TLogTool.Error("【UdpReceive】发送消息时发生错误：" + ex.Message);
            }
        }
        
        /// <summary>
        /// 关闭UDP广播
        /// </summary>
        public void Close()
        {
            try
            {
                if (_udpReceive != null)
                {
                    _udpReceive.Close();
                    _udpReceive = null;
                    TLogTool.Info("【UdpReceive】关闭UDP接收消息");
                } 
            }
            catch (Exception ex)
            {
                TLogTool.Error("【UdpReceive】关闭UDP时生错误:" + ex);
            }
        }
        
        //接收到广播消息后的回调方法
        private void OnReceiveComplete(string msg)
        {
            BroadcastPacket packet = TinyUtility.Json.ToObject<BroadcastPacket>(msg);
            //判断是否是同一个应用
            if (packet.productName == _feedbackPacket.productName)
            {
                //向广播消息的一端，发送本地IP和连接的端口号
                string feedbackMsg = TinyUtility.Json.ToJson(_feedbackPacket);
                SendMsg(new IPEndPoint(IPAddress.Parse(packet.ip), packet.port), Encoding.UTF8.GetBytes(feedbackMsg));
                
                _onReceivedMsg?.Invoke(packet);
            }
            else
            {
                TLogTool.Info($"【UdpReceive】收到信息的应用（{packet.productName}）与当前应用不匹配（{_feedbackPacket.productName}）");
            }
        }
    }
}
